package com.datagateway.component;

import com.datagateway.config.DataGatewayProperties;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Groovy脚本引擎组件
 * 负责加载、缓存和执行Groovy转换脚本
 * 
 * @author Data Gateway Team
 * @version 1.0.0
 */
@Component
public class GroovyScriptEngine {

    private static final Logger logger = LoggerFactory.getLogger(GroovyScriptEngine.class);

    @Autowired
    private DataGatewayProperties properties;

    @Autowired
    private ResourceLoader resourceLoader;

    /**
     * 脚本缓存
     */
    private final Map<String, Script> scriptCache = new ConcurrentHashMap<>();

    /**
     * 缓存启用状态
     */
    private boolean cacheEnabled = true;

    /**
     * 缓存大小限制
     */
    private int cacheSize = 100;

    /**
     * Groovy Shell实例
     */
    private GroovyShell groovyShell;

    /**
     * 初始化脚本引擎
     */
    @PostConstruct
    public void init() {
        // 创建Groovy Shell
        groovyShell = new GroovyShell();
        
        // 预加载默认脚本
        loadDefaultScript();
        
        logger.info("Groovy脚本引擎初始化完成");
    }

    /**
     * 预加载默认脚本
     */
    private void loadDefaultScript() {
        try {
            String defaultScriptName = properties.getGroovy().getDefaultScript();
            loadScript("default", defaultScriptName);
            logger.info("默认脚本加载成功: {}", defaultScriptName);
        } catch (Exception e) {
            logger.warn("默认脚本加载失败，将使用内置默认脚本", e);
            createDefaultScript();
        }
    }

    /**
     * 创建内置默认脚本
     */
    private void createDefaultScript() {
        String defaultScript = "// 默认数据转换脚本\n" +
            "import groovy.json.JsonSlurper\n" +
            "import groovy.json.JsonBuilder\n" +
            "\n" +
            "def transform(data) {\n" +
            "    try {\n" +
            "        // 解析JSON数据\n" +
            "        def jsonSlurper = new JsonSlurper()\n" +
            "        def jsonData = jsonSlurper.parseText(data)\n" +
            "        \n" +
            "        // 添加处理时间戳\n" +
            "        jsonData.processTime = new Date().format('yyyy-MM-dd HH:mm:ss.SSS')\n" +
            "        \n" +
            "        // 添加数据来源标识\n" +
            "        jsonData.source = 'kafka'\n" +
            "        \n" +
            "        // 转换为JSON字符串\n" +
            "        def jsonBuilder = new JsonBuilder(jsonData)\n" +
            "        return jsonBuilder.toString()\n" +
            "        \n" +
            "    } catch (Exception e) {\n" +
            "        // 如果解析失败，返回原始数据\n" +
            "        return data\n" +
            "    }\n" +
            "}";
        
        try {
            Script script = groovyShell.parse(defaultScript);
            scriptCache.put("default", script);
            logger.info("内置默认脚本创建成功");
        } catch (Exception e) {
            logger.error("创建内置默认脚本失败", e);
        }
    }

    /**
     * 加载脚本
     * 
     * @param scriptName 脚本名称
     * @param scriptFile 脚本文件名
     */
    public void loadScript(String scriptName, String scriptFile) {
        try {
            String scriptPath = properties.getGroovy().getScriptPath() + scriptFile;
            Resource resource = resourceLoader.getResource(scriptPath);
            
            if (!resource.exists()) {
                throw new IOException("脚本文件不存在: " + scriptPath);
            }
            
            String scriptContent = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
            Script script = groovyShell.parse(scriptContent);
            
            scriptCache.put(scriptName, script);
            logger.info("脚本加载成功: {} -> {}", scriptName, scriptFile);
            
        } catch (Exception e) {
            logger.error("脚本加载失败: {} -> {}", scriptName, scriptFile, e);
            throw new RuntimeException("脚本加载失败", e);
        }
    }

    /**
     * 设置缓存启用状态
     * 
     * @param cacheEnabled 是否启用缓存
     */
    public void setCacheEnabled(boolean cacheEnabled) {
        this.cacheEnabled = cacheEnabled;
        logger.info("Groovy脚本缓存状态已更新: {}", cacheEnabled);
    }

    /**
     * 更新缓存大小
     * 
     * @param cacheSize 新的缓存大小
     */
    public void updateCacheSize(int cacheSize) {
        this.cacheSize = cacheSize;
        // 如果当前缓存大小超过新限制，清理缓存
        if (scriptCache.size() > cacheSize) {
            scriptCache.clear();
            logger.info("Groovy脚本缓存已清理，新缓存大小: {}", cacheSize);
        } else {
            logger.info("Groovy脚本缓存大小已更新: {}", cacheSize);
        }
    }

    /**
     * 重新加载脚本
     */
    public void reloadScripts() {
        scriptCache.clear();
        logger.info("Groovy脚本缓存已清理，将重新加载脚本");
    }

    /**
     * 执行数据转换
     * 
     * @param data 原始数据
     * @param scriptName 脚本名称
     * @return 转换后的数据
     */
    public String transformData(String data, String scriptName) {
        long startTime = System.currentTimeMillis();
        
        try {
            // 获取脚本
            Script script = getScript(scriptName);
            
            // 创建绑定上下文
            Binding binding = new Binding();
            binding.setVariable("data", data);
            binding.setVariable("timestamp", System.currentTimeMillis());
            binding.setVariable("date", new java.util.Date());
            
            // 设置脚本绑定
            script.setBinding(binding);
            
            // 执行转换
            Object result = script.run();
            
            long processingTime = System.currentTimeMillis() - startTime;
            logger.debug("数据转换完成，脚本: {}, 耗时: {}ms", scriptName, processingTime);
            
            return result != null ? result.toString() : data;
            
        } catch (Exception e) {
            long processingTime = System.currentTimeMillis() - startTime;
            logger.error("数据转换失败，脚本: {}, 耗时: {}ms", scriptName, processingTime, e);
            throw new RuntimeException("数据转换失败", e);
        }
    }

    /**
     * 执行数据转换（使用默认脚本）
     * 
     * @param data 原始数据
     * @return 转换后的数据
     */
    public String transformData(String data) {
        return transformData(data, "default");
    }

    /**
     * 获取脚本
     * 
     * @param scriptName 脚本名称
     * @return 脚本对象
     */
    private Script getScript(String scriptName) {
        Script script = scriptCache.get(scriptName);
        if (script == null) {
            // 如果脚本不存在，尝试加载
            loadScript(scriptName, scriptName + ".groovy");
            script = scriptCache.get(scriptName);
        }
        
        if (script == null) {
            // 如果仍然不存在，使用默认脚本
            script = scriptCache.get("default");
            logger.warn("脚本不存在，使用默认脚本: {}", scriptName);
        }
        
        return script;
    }

    /**
     * 重新加载脚本
     * 
     * @param scriptName 脚本名称
     * @param scriptFile 脚本文件名
     */
    public void reloadScript(String scriptName, String scriptFile) {
        try {
            loadScript(scriptName, scriptFile);
            logger.info("脚本重新加载成功: {}", scriptName);
        } catch (Exception e) {
            logger.error("脚本重新加载失败: {}", scriptName, e);
        }
    }

    /**
     * 清除脚本缓存
     */
    public void clearCache() {
        scriptCache.clear();
        logger.info("脚本缓存已清除");
    }

    /**
     * 获取缓存状态
     * 
     * @return 缓存状态信息
     */
    public Map<String, Object> getCacheStatus() {
        Map<String, Object> status = new ConcurrentHashMap<>();
        status.put("cacheSize", scriptCache.size());
        status.put("cachedScripts", scriptCache.keySet());
        status.put("cacheEnabled", properties.getGroovy().isCacheEnabled());
        return status;
    }

    /**
     * 验证脚本语法
     * 
     * @param scriptContent 脚本内容
     * @return true如果语法正确
     */
    public boolean validateScript(String scriptContent) {
        try {
            groovyShell.parse(scriptContent);
            return true;
        } catch (Exception e) {
            logger.warn("脚本语法验证失败", e);
            return false;
        }
    }
}
